﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;


namespace FGK
{
    class ParserOBJ
    {
        
        String path;
        
        public ParserOBJ()
        {
            this.path = "obj\\scena.obj";
        }

        public ParserOBJ(string OBJpath)
        {
            this.path = OBJpath;
        }

        public float[] ParseStringArrayToFloat(string[] strArrIn)
        {
            float[] retfloatArray = new float[strArrIn.Length];
            for (int i = 0; i < strArrIn.Length; i++)
            {
                var nfi = new System.Globalization.NumberFormatInfo() { NumberDecimalSeparator = "." };
                //FUCK YOU DOT, REALLY, FUCK YOU...
                retfloatArray[i] = float.Parse(strArrIn[i], nfi);
            }
            return retfloatArray;
        }

        public Material ParseMTL(string mtlFilePath, string materialName)
        {
            Material retMat = new Material();
            StreamReader mr = new StreamReader(mtlFilePath);       
            //string name= "newMat";
            //float tr=0.0f, illum=2.0f;
            string readL = mr.ReadLine();
            while (readL != null)
            {
                if (readL.StartsWith("newmtl"))
                {
                    string txt;
                    string[] lineArray;
                    txt = readL.Substring(7);
                    if (txt == materialName)
                    {
                        var nfi = new System.Globalization.NumberFormatInfo() { NumberDecimalSeparator = "." };
                        float Ns, Ni, d, Tr, illum;
                        float[] Tf, Ka, Kd, Ks, Ke;
                        

                        readL = mr.ReadLine();//Ns
                        txt = readL.Substring(3);
                        Ns = float.Parse(txt, nfi);

                        readL = mr.ReadLine();//Ni
                        txt = readL.Substring(3);
                        Ni = float.Parse(txt, nfi);

                        readL = mr.ReadLine();//d
                        txt = readL.Substring(2);
                        d = float.Parse(txt, nfi);

                        readL = mr.ReadLine();//Tr
                        txt = readL.Substring(3);
                        Tr = float.Parse(txt, nfi);

                        readL = mr.ReadLine();//Tf
                        txt = readL.Substring(4);
                        lineArray = txt.Split(null);
                        Tf = ParseStringArrayToFloat(lineArray);

                        readL = mr.ReadLine();//illum
                        txt = readL.Substring(6);
                        illum = float.Parse(txt, nfi);

                        readL = mr.ReadLine();//Ka
                        txt = readL.Substring(4);
                        lineArray = txt.Split(null);
                        Ka = ParseStringArrayToFloat(lineArray);

                        readL = mr.ReadLine();//Kd
                        txt = readL.Substring(4);
                        lineArray = txt.Split(null);
                        Kd = ParseStringArrayToFloat(lineArray);

                        readL = mr.ReadLine();//Ks
                        txt = readL.Substring(4);
                        lineArray = txt.Split(null);
                        Ks = ParseStringArrayToFloat(lineArray);

                        readL = mr.ReadLine();//Ke
                        txt = readL.Substring(4);
                        lineArray = txt.Split(null);
                        Ke = ParseStringArrayToFloat(lineArray);

                        return new Material(new LightIntensity(Ka[0], Ka[1], Ka[2]),new LightIntensity(Kd[0], Kd[1], Kd[2]),new LightIntensity(Ks[0], Ks[1], Ks[2]),Ns);
                    }

                }
                readL = mr.ReadLine();
            }

            
            return retMat;
        }

        public void printScene(List<Vector3> v, List<Vector3> vn, List<Vector3> f, List<Vector2> tex)
        {
            Console.WriteLine("Vertices ("+v.Count+"):");
            for (int i = 0; i < v.Count; i++)
            {
                Console.WriteLine(v.ElementAt(i).ToString());
            }
            Console.WriteLine("Normals (" + vn.Count + "):");
            for (int i = 0; i < vn.Count; i++)
            {
                Console.WriteLine(vn.ElementAt(i).ToString());
            }
            Console.WriteLine("Faces (" + f.Count + "):");
            for (int i = 0; i < f.Count; i++)
            {
                Console.WriteLine(f.ElementAt(i).ToString());
            }
            Console.WriteLine("Texture Coords (" + tex.Count + "):");
            for (int i = 0; i < tex.Count; i++)
            {
                Console.WriteLine(tex.ElementAt(i).ToString());
            }
        }

        public List<Mesh> ParseOBJ()
        {
            StreamReader reader = new StreamReader(path);
            Console.WriteLine(DateTime.Now.TimeOfDay.ToString() + " Parsing " + path + "...");
            List<Mesh> parsedMeshes = new List<Mesh>();

            //List<Vector3> vertexList = new List<Vector3>();
            //List<Vector3> normalList = new List<Vector3>();
            //List<Vector3> faceList = new List<Vector3>();
            //List<Vector2> texCoordList = new List<Vector2>();

            Mesh mesh = new Mesh();
            string mtlFile= "scena.mtl";
            int matCount = -1;

            
            string readL = reader.ReadLine();
            while (readL != null)
            {
                //DEBUG: Wypisuje linijke wczytana z pliku
                //Console.WriteLine(readL);

                if (!readL.StartsWith("#"))
                {
                    string txt;
                    string[] lineArray;
                    float[] fa;

                    // texture coords
                    if (readL.StartsWith("vt"))
                    {
                        txt = readL.Substring(3);
                        lineArray = txt.Split(null);
                        fa = ParseStringArrayToFloat(lineArray);
                        Vector2 coord = new Vector2(fa[0], fa[1]);
                        mesh.texCoordList.Add(coord);
                    }
                    else
                    {
                        Vector3 vertex;
                        // vertex normals
                        if (readL.StartsWith("vn"))
                        {
                            txt = readL.Substring(3);
                            lineArray = txt.Split(null);
                            fa = ParseStringArrayToFloat(lineArray);
                            vertex = new Vector3(fa[0], fa[1], fa[2]);
                            mesh.normalList.Add(vertex);
                        }
                        else
                        {
                            // vertices
                            if (readL.StartsWith("v"))
                            {
                                txt = readL.Substring(3);
                                lineArray = txt.Split(null);
                                fa = ParseStringArrayToFloat(lineArray);
                                vertex = new Vector3(fa[0], fa[1], fa[2]);
                                mesh.vertexList.Add(vertex);
                            }
                            else
                            {
                                if (readL.StartsWith("f"))
                                {
                                    string[] a, b, c;
                                    txt = readL.Substring(2);
                                    lineArray = txt.Split(null);
                                    a = lineArray[0].Split(new char[] { '/' });
                                    b = lineArray[1].Split(new char[] { '/' });
                                    c = lineArray[2].Split(new char[] { '/' });
                                    Triangle tri = new Triangle(mesh.vertexList.ElementAt(int.Parse(a[0])-1), mesh.vertexList.ElementAt(int.Parse(b[0])-1), mesh.vertexList.ElementAt(int.Parse(c[0])-1), mesh.normalList.ElementAt(int.Parse(a[2])-1));
                                    //a[0],b[0],c[0],a[2]
                                    mesh.faceList.Add(tri);
                                }
                                if (readL.StartsWith("g"))
                                {                                     
                                    if (mesh.name != "newMesh")
                                    {
                                        txt = readL.Substring(2);
                                        if (mesh.name != txt)
                                        {
                                            parsedMeshes.Add(mesh);
                                            mesh = new Mesh(txt);
                                            matCount++;
                                        }
                                    }
                                    else
                                    {
                                        txt = readL.Substring(2);
                                        if (txt == "newMesh") txt = "newMesh01"; //someone's very funny :p
                                        mesh.name = txt;
                                    }
                                }
                                if (readL.StartsWith("mtllib"))
                                {
                                    txt = readL.Substring(7);
                                    mtlFile = "obj\\"+txt;
                                }
                                if (readL.StartsWith("usemtl"))
                                {
                                    
                                    txt = readL.Substring(7);
                                    //Console.WriteLine("Loading Mat " + txt);
                                    mesh.material = ParseMTL(mtlFile, txt);
                                }
                            }
                        }            
                    }
                }
                
                readL = reader.ReadLine();
            }
            parsedMeshes.Add(mesh);
            Console.WriteLine(DateTime.Now.TimeOfDay.ToString() + " Parsing finished");

            //DEBUG: Wypisuje zawartosc list wierzcholkow normalnych itp wczytanych z obj
            //printScene(vertexList, normalList, faceList, texCoordList);
            //foreach (Mesh i in parsedMeshes) Console.WriteLine(i.ToString());
            return parsedMeshes;
        }
    }
}
